<!doctype html>
<html>
  <head>
    <title>AudioParam.automationRate tests</title>
    <script src="../../../resources/testharness.js"></script>
    <script src="../../../resources/testharnessreport.js"></script>
    <script src="../../../webaudio/resources/audit-util.js"></script>
    <script src="../../../webaudio/resources/audit.js"></script>
  </head>

  <body>
    <script>
      // For each node that has an AudioParam, verify that the default
      // |automationRate| has the expected value and that we can change it or
      // throw an error if it can't be changed.

      // Any valid sample rate is fine; we don't actually render anything in the
      // tests.
      let sampleRate = 8000;

      let audit = Audit.createTaskRunner();

      // Array of tests.  Each test is a dictonary consisting of the name of the
      // node and an array specifying the AudioParam's of the node.  This array
      // in turn gives the name of the AudioParam, the default value for the
      // |automationRate|, and whether it is fixed (isFixed).
      const tests = [
        {
          nodeName: 'AudioBufferSourceNode',
          audioParams: [
            {name: 'detune', defaultRate: 'k-rate', isFixed: true},
            {name: 'playbackRate', defaultRate: 'k-rate', isFixed: true}
          ]
        },
        {
          nodeName: 'BiquadFilterNode',
          audioParams: [
            {name: 'frequency', defaultRate: 'a-rate', isFixed: false},
            {name: 'detune', defaultRate: 'a-rate', isFixed: false},
            {name: 'Q', defaultRate: 'a-rate', isFixed: false},
            {name: 'gain', defaultRate: 'a-rate', isFixed: false},
          ]
        },
        {
          nodeName: 'ConstantSourceNode',
          audioParams: [{name: 'offset', defaultRate: 'a-rate', isFixed: false}]
        },
        {
          nodeName: 'DelayNode',
          audioParams:
              [{name: 'delayTime', defaultRate: 'a-rate', isFixed: false}]
        },
        {
          nodeName: 'DynamicsCompressorNode',
          audioParams: [
            {name: 'threshold', defaultRate: 'k-rate', isFixed: true},
            {name: 'knee', defaultRate: 'k-rate', isFixed: true},
            {name: 'ratio', defaultRate: 'k-rate', isFixed: true},
            {name: 'attack', defaultRate: 'k-rate', isFixed: true},
            {name: 'release', defaultRate: 'k-rate', isFixed: true}
          ]
        },
        {
          nodeName: 'GainNode',
          audioParams: [{name: 'gain', defaultRate: 'a-rate', isFixed: false}]
        },
        {
          nodeName: 'OscillatorNode',
          audioParams: [
            {name: 'frequency', defaultRate: 'a-rate', isFixed: false},
            {name: 'detune', defaultRate: 'a-rate', isFixed: false}
          ]
        },
        {
          nodeName: 'PannerNode',
          audioParams: [
            {name: 'positionX', defaultRate: 'a-rate', isFixed: false},
            {name: 'positionY', defaultRate: 'a-rate', isFixed: false},
            {name: 'positionZ', defaultRate: 'a-rate', isFixed: false},
            {name: 'orientationX', defaultRate: 'a-rate', isFixed: false},
            {name: 'orientationY', defaultRate: 'a-rate', isFixed: false},
            {name: 'orientationZ', defaultRate: 'a-rate', isFixed: false},
          ]
        },
        {
          nodeName: 'StereoPannerNode',
          audioParams: [{name: 'pan', defaultRate: 'a-rate', isFixed: false}]
        },
      ];

      tests.forEach(test => {
        // Define a separate test for each test entry.
        audit.define(test.nodeName, (task, should) => {
          let context = new OfflineAudioContext(
              {length: sampleRate, sampleRate: sampleRate});
          // Construct the node and test each AudioParam of the node.
          let node = new window[test.nodeName](context);
          test.audioParams.forEach(param => {
            testAudioParam(
                should, {nodeName: test.nodeName, node: node, param: param});
          });

          task.done();
        });
      });

      // AudioListener needs it's own special test since it's not a node.
      audit.define('AudioListener', (task, should) => {
        let context = new OfflineAudioContext(
            {length: sampleRate, sampleRate: sampleRate});

        [{name: 'positionX', defaultRate: 'a-rate', isFixed: false},
         {name: 'positionY', defaultRate: 'a-rate', isFixed: false},
         {name: 'positionZ', defaultRate: 'a-rate', isFixed: false},
         {name: 'forwardX', defaultRate: 'a-rate', isFixed: false},
         {name: 'forwardY', defaultRate: 'a-rate', isFixed: false},
         {name: 'forwardZ', defaultRate: 'a-rate', isFixed: false},
         {name: 'upX', defaultRate: 'a-rate', isFixed: false},
         {name: 'upY', defaultRate: 'a-rate', isFixed: false},
         {name: 'upZ', defaultRate: 'a-rate', isFixed: false},
        ].forEach(param => {
          testAudioParam(should, {
            nodeName: 'AudioListener',
            node: context.listener,
            param: param
          });
        });
        task.done();
      });

      audit.run();

      function testAudioParam(should, options) {
        let param = options.param;
        let audioParam = options.node[param.name];
        let defaultRate = param.defaultRate;

        // Verify that the default value is correct.
        should(
            audioParam.automationRate,
            `Default ${options.nodeName}.${param.name}.automationRate`)
            .beEqualTo(defaultRate);

        // Try setting the rate to a different rate.  If the |automationRate|
        // is fixed, expect an error.  Otherwise, expect no error and expect
        // the value is changed to the new value.
        let newRate = defaultRate === 'a-rate' ? 'k-rate' : 'a-rate';
        let setMessage = `Set ${
                                options.nodeName
                              }.${param.name}.automationRate to "${newRate}"`

        if (param.isFixed) {
          should(() => audioParam.automationRate = newRate, setMessage)
              .throw(DOMException, 'InvalidStateError');
        }
        else {
          should(() => audioParam.automationRate = newRate, setMessage)
              .notThrow();
          should(
              audioParam.automationRate,
              `${options.nodeName}.${param.name}.automationRate`)
              .beEqualTo(newRate);
        }
      }
    </script>
  </body>
</html>
